/*******************************************
Copyright: 2018,Syber-sense Tech.Co, Ltd   (refer to the wpa source code)
File name:wifi.c
Description:There are many APIs which can be used to realize wifi common functions,
eg:  wifi scan/wifi connect/wifi disconnect/wifi remove/wifi dhcp and so on

Node:  these function based on wpa_supplicant,they communicate with udp socket,,so need run the follows command at first:
wpa_supplicant -d -Dwext -i wlan0 -c  /etc/wpa_supplicant.conf &

Author:Richd
Version:1.0
Date:2019-07-04
History:
2019-07-08  cily  update from wpa 2.8 version
 *******************************************/
#include "wifi.h"
#include <signal.h>
#include <stdbool.h>
#include <string.h>
#include <sys/prctl.h>
#include "globalAPI.h"
#include "list.h"
#include "c_timer.h"
#include "zlog.h"

static pthread_mutex_t wifiMutex = PTHREAD_MUTEX_INITIALIZER;  // prevent function re-enter

char *ctrl_iface     = NULL;
char *ctrl_iface_dir = NULL;

struct wpa_ctrl *monitor_conn;
struct wpa_ctrl *ctrl_conn;

#define CONFIG_CTRL_IFACE_CLIENT_DIR "/tmp"
#define CONFIG_CTRL_IFACE_CLIENT_PREFIX "wpa_ctrl_"

/******************* define function ***********************/
#define os_free(p) free((p))
#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n))
#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n))

/***************** function *************************************/
struct wpa_ctrl *wpa_ctrl_open(const char *ctrl_path);
void InitWpa(void);
int openCtrlConnect(const char *ifname);
void wpa_ctrl_close(struct wpa_ctrl *ctrl);

static scan_t scanAP;
static bool scanAction = false;

/**
 **function:wpa_ctrl_request
 **Describe: it used to send a cmd to system and get the replay
 **param:ctrl [ in ] :handle of the connect which is used to communicate between them
 **param:cmd_len [ in ] : the size of the cmd
 **param:reply [ in ] :the request reply msg buf
 **param:reply_len [ out ]:the size of the request reply msg buf
 **param:msg_cb [ in ]:callback  not used(NULL)
 **return: <-1----> error   -1--->time out    0: succeed
 */
int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, char *reply, size_t *reply_len, void (*msg_cb)(char *msg, size_t len))
{
    struct timeval tv;
    struct os_reltime started_at;
    int res;
    fd_set rfds;
    const char *_cmd = NULL;
    int retry_count  = 0;

    size_t _cmd_len;

    {
        _cmd     = cmd;
        _cmd_len = cmd_len;
    }

    errno           = 0;
    started_at.sec  = 0;
    started_at.usec = 0;
retry_send:
    if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) {
        if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK) {
            /*
             * Must be a non-blocking socket... Try for a bit
             * longer before giving up.
             */
            if (started_at.sec == 0)
                os_get_reltime(&started_at);
            else {
                struct os_reltime n;
                os_get_reltime(&n);
                /* Try for a few seconds. */
                if (os_reltime_expired(&n, &started_at, 5))
                    goto send_err;
            }
            os_sleep(1, 0);
            goto retry_send;
        }
    send_err:

        return -1;
    }

    for (;;) {
        tv.tv_sec  = 5;
        tv.tv_usec = 0;
        FD_ZERO(&rfds);
        FD_SET(ctrl->s, &rfds);
        res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
        if (res < 0 && errno == EINTR) {
            if (retry_count++ >= 3) {
                dzlog_debug("Retries limit hit breaking out\n");
                return -1;
            }
            dzlog_debug("EINTR received polling again\n");
            continue;
        }
        if (res < 0)
            return res;
        if (FD_ISSET(ctrl->s, &rfds)) {
            res = recv(ctrl->s, reply, *reply_len, 0);
            if (res < 0)
                return res;
            if ((res > 0 && reply[0] == '<') || (res > 6 && strncmp(reply, "IFNAME=", 7) == 0)  // added by cily 2018-07-08
            ) {
                /* This is an unsolicited message from
                 * wpa_supplicant, not the reply to the
                 * request. Use msg_cb to report this to the
                 * caller. */
                if (msg_cb) {
                    /* Make sure the message is nul
                     * terminated. */
                    if ((size_t)res == *reply_len)
                        res = (*reply_len) - 1;
                    reply[res] = '\0';
                    msg_cb(reply, res);
                }
                continue;
            }
            *reply_len = res;
            break;
        } else {
            return -2;
        }
    }
    return 0;
}

/**
 ** after connectd a network or hot spot,request a ip address and update the /etc/resolv.conf file
 */
int redbee_dhcp_get(char *interface)
{
    dzlog_debug("start dhcp");
    char result[256] = {0};
    char cmd[128]    = {0};
    int ret          = 0;

    if (interface == NULL)
        return -1;

    snprintf(cmd, sizeof(cmd), "dhclient %s", interface);
    ret = shellCommand(cmd, result, sizeof(result), true, false);
    if (ret == 0) {
        memset(cmd, 0, sizeof(cmd));
        sprintf(cmd, "%s", "kill -9 $(pidof dhclient)");
        ret = shellCommand(cmd, result, sizeof(result), true, false);

        if (redbee_getIpAddr(interface) != NULL) {
            dzlog_debug("%s dhcp success\n", interface);
            return 0;
        }
    }
    dzlog_debug("%s dhcp failed\n", interface);
    return ret;
}

int redbee_dhcp_release(char *interface)
{
    dzlog_debug("release dhcp");
    char result[256] = {0};
    char cmd[128]    = {0};
    int ret          = 0;

    if (interface == NULL)
        return -1;

    snprintf(cmd, sizeof(cmd), "dhclient -r %s", interface);
    ret = shellCommand(cmd, result, sizeof(result), true, false);
    if (ret == 0) {
        if (redbee_getIpAddr(interface) == NULL) {
            dzlog_debug("%s dhcp release success\n", interface);
            return 0;
        }
    }
    dzlog_debug("%s dhcp release failed\n", interface);
    return ret;
}

/**
 **function:CtrlRequest
Descirbe: this function can not re-enter,it used to send a cmd to system and get the result
 **param:cmd [ in ] :the cmd request to wap_supplicant
 **param:buf [ out ]:the respone msg from wap_supplicant
 **param:buflen [ in ]:the size of the respone msg
 **return: <-1----> error   -1--->time out    0: succeed
 */
int CtrlRequest(const char *cmd, char *buf, size_t *buflen)
{
    int ret               = 0;
    char connIface[50]    = {0};
    struct wpa_ctrl *ctrl = NULL;

    pthread_mutex_lock(&wifiMutex);

    snprintf(connIface, sizeof(connIface), "%s/%s", ctrl_iface_dir, ctrl_iface);
    ctrl = wpa_ctrl_open(connIface);
    if (ctrl == NULL) {
        dzlog_debug("'%s' open failed.\n", connIface);
        pthread_mutex_unlock(&wifiMutex);
        return -1;
    }
    ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, buflen, NULL);
    if (ret < 0) {
        dzlog_debug("wifi '%s' command timed out.\n", cmd);
        wpa_ctrl_close(ctrl);
        pthread_mutex_unlock(&wifiMutex);
        return -1;
    }

    wpa_ctrl_close(ctrl);

    pthread_mutex_unlock(&wifiMutex);
    return 0;
}

bool ssidAlreadyInTheList(char *ssid, List *list)
{
    int i = 0;
    /*Can't add a NULL ssid to List.*/
    if (NULL == ssid) {
        return true;
    }
    for (i = 0; i < list_count(list); i++) {
        ScanAPInfo *ap = list_data(list, i);
        if (!strcmp(ssid, ap->ssid)) {
            // dzlog_debug("find AP %s but already exist", ssid);
            return true;
        }
        //		dzlog_debug("index(%d) AP (%s) already exist\n",i,ssid);
    }
    return false;
}

bool bssidAlreadyInTheList(char *bssid, List *list)
{
    int i = 0;
    /*Can't add a NULL ssid to List.*/
    if (NULL == bssid) {
        return true;
    }
    for (i = 0; i < list_count(list); i++) {
        ScanAPInfo *ap = list_data(list, i);
        if (!strcmp(bssid, ap->bssid)) {
            // dzlog_warn("find AP %s but already exist", bssid);
            return true;
        }
    }
    return false;
}

/**
 ** remove the oldest hotspot which had connected before
 */

int connectHistoryList(List *list)
{
    char delims[]     = "\n";
    char Linedelims[] = "\t";
    char *tmpbuff     = NULL;
    char *Linebuff    = NULL;
    char *ssid        = NULL;
    char reply[2048]  = {0};
    size_t reply_len;
    ConnectedAPInfo connAp = {0};

    reply_len = sizeof(reply) - 1;
    bzero(reply, sizeof(reply));

    if (CtrlRequest("LIST_NETWORKS", reply, &reply_len) < 0)
        return -1;

    reply[reply_len] = '\0';

    tmpbuff = redbee_strtok(reply, delims);
    while (tmpbuff != NULL) {
        tmpbuff = redbee_strtok(NULL, delims);
        if (tmpbuff == NULL)
            break;

        Linebuff = strtok(tmpbuff, Linedelims);
        while (Linebuff != NULL) {
            connAp.id = atoi(Linebuff);
            ssid      = strtok(NULL, Linedelims);
            if (ssid) {
                strncpy(connAp.ssid, ssid, sizeof(connAp.ssid));
            }
            list_append(&connAp, list, sizeof(connAp));
            break;
        }
    }

    return 0;
}

void removeOldestConnected()
{
    List *connap;
    list_create(&connap);
    connectHistoryList(connap);
}

int ParseScanResult(char *buf, List *list)
{
    int i             = 0;
    int totalCount    = 0;
    char delims[]     = "\n";
    char lineDelims[] = "=";
    char *tmpbuff     = NULL;
    char *bufstart    = NULL;

    char *ApInfo[] = {
        "bssid",
        "freq",
        "level",
        "flags",
        "ssid",
    };

    ScanAPInfo ap = {0};

    tmpbuff      = redbee_strtok(buf, delims);
    List *apList = list;
    while (tmpbuff != NULL) {
        bufstart = strtok(tmpbuff, lineDelims);
        for (i = 0; i < APINFO_TOTAL_ITEMS; i++) {
            if (bufstart != NULL) {
                if (strcmp(bufstart, ApInfo[i]) == 0) {
                    bufstart = strtok(NULL, lineDelims);
                    if (bufstart != NULL) {
                        break;
                    }
                }
            }
        }

        if (i == 0) {
            memcpy(ap.bssid, bufstart, sizeof(ap.bssid));
            totalCount++;
        } else if (i == 1) {
            ap.freq = atoi(bufstart);
            totalCount++;
        } else if (i == 2) {
            ap.level = atoi(bufstart);
            totalCount++;
        } else if (i == 3) {
            memcpy(ap.flag, bufstart, sizeof(ap.flag));
            totalCount++;
        } else if (i == 4) {
            memcpy(ap.ssid, bufstart, sizeof(ap.ssid));
            totalCount++;
        }

        if (NULL != strstr(ap.flag, "WPA-PSK")) {
            ap.Keyopen = AUTH_WPA_PSK;
        } else if (NULL != strstr(ap.flag, "WPA2-PSK")) {
            ap.Keyopen = AUTH_WPA2_PSK;
        } else if (NULL != strstr(ap.flag, "WEP")) {
            ap.Keyopen = AUTH_NONE_WEP;
        } else {
            ap.Keyopen = AUTH_NONE_OPEN;
        }

        if (ssidAlreadyInTheList(ap.ssid, apList) || bssidAlreadyInTheList(ap.bssid, apList)) {
            // nothing

        } else if (totalCount == APINFO_TOTAL_ITEMS) {
            list_append(&ap, apList, sizeof(ScanAPInfo));
            totalCount = 0;
            memset(&ap, 0, sizeof(ap));
        }

        tmpbuff = redbee_strtok(NULL, delims);
    }

    return 0;
}

static void strongestSignalApSort(List *apList)
{
    int i     = 0;
    int j     = 0;
    int count = list_count(apList);
    for (i = 0; i < count - 1; i++) {
        for (j = 0; j < count - i - 1; j++) {
            ScanAPInfo *ap   = list_data(apList, j);
            ScanAPInfo *next = list_data(apList, j + 1);
            if (ap->level < next->level) {
                list_swap(j, j + 1, apList);
            }
        }
    }
}

/*Auto connect to the Strongest signal AP.*/
void SelectSignalAP(autoConnCb cb, char sec, List *list)
{
    char *ssid = NULL;
    int ret    = 0;

    ssid = redbee_wifi_connectSsid();
    if (NULL != ssid) {
        dzlog_debug("already connected to %s", ssid);
        char *ip = redbee_getIpAddr(ctrl_iface);
        if (NULL == ip) {
            if (redbee_dhcp_get(ctrl_iface) != 0) {
                dzlog_debug("dhcp address assigned failed\n");
            }
        } else {
            dzlog_debug("ip(%s) address assigned\n", ip);
        }
    } else {
        for (int i = 0; i < list_count(list); i++) {
            ScanAPInfo *ap = list_data(list, i);
            if (NULL != ap) {
                ret = redbee_wifi_connectOldAP(ap->ssid, sec);
                if (ret == 0) {
                    if (cb != NULL)
                        cb(ap->ssid);

                    /*Auto conneted success, stop, else, continue*/
                    dzlog_debug("Auto connected wifi to %s", ap->ssid);
                    return;
                }
            }
        }
    }
    return;
}

int UpdateResult(scan_t apScan)
{
    char reply[1024] = {0};
    size_t reply_len;
    int index    = 0;
    char cmd[20] = {0};

    List *list = NULL;
    list       = list_create(&list);
    index      = 0;
    while (1) {
        snprintf(cmd, sizeof(cmd), "BSS %d", index++);
        if (index > 1000)
            break;

        reply_len = sizeof(reply) - 1;
        if (CtrlRequest(cmd, reply, &reply_len) < 0) {
            list_destroy(&list);
            return -1;
        }

        reply[reply_len] = '\0';
        if (strlen(reply)) {
            ParseScanResult(reply, list);  // save scan result in a list
        } else {
            continue;
        }
        usleep(10000);
    }

    dzlog_debug("wifi scan list count:%d\n", list_count(list));
    if (!isEmpty(list)) {
        strongestSignalApSort(list);
        if (apScan.scanCb != NULL)
            apScan.scanCb(list);
    }

    if (!isEmpty(list) && apScan.connSec) {
        SelectSignalAP(apScan.autoConnCbCallback, apScan.connSec, list);
    }

    list_destroy(&list);
    return 0;
}

int ParseListWorks(char *buf, char *selectssid)
{
    char delims[]     = "\n";
    char Linedelims[] = "\t";
    char *tmpbuff     = NULL;
    char *Linebuff    = NULL;
    int id            = 0;
    char *ssid        = NULL;

    tmpbuff = redbee_strtok(buf, delims);
    while (tmpbuff != NULL) {
        tmpbuff = redbee_strtok(NULL, delims);
        if (tmpbuff == NULL)
            break;

        Linebuff = strtok(tmpbuff, Linedelims);
        while (Linebuff != NULL) {
            id   = atoi(Linebuff);
            ssid = strtok(NULL, Linedelims);
            if ((ssid && selectssid) && (strcmp(ssid, selectssid) == 0)) {
                return id;
            }
            break;
        }
    }
    return -1;
}

/**
 **function:redbee_wifi_netListWorks
 id		ssid		    			bssid		flag
 0	Syber-senseany	              any         [ CURRENT ]
 **Describe:whether the ssid has connected before
 **return:<0 dont connect before   >=0: had connect before
 */
int redbee_wifi_netListWorks(char *ssid)
{
    char reply[2048] = {0};
    int ret          = -1;
    size_t reply_len;

    if (ssid == NULL) {
        return -1;
    }

    reply_len = sizeof(reply) - 1;
    bzero(reply, sizeof(reply));

    if (CtrlRequest("LIST_NETWORKS", reply, &reply_len) < 0)
        return -1;

    reply[reply_len] = '\0';

    ret = ParseListWorks(reply, ssid);

    return ret;
}

int SelectNetwork(int Netid)
{
    char reply[10]   = {0};
    char cmd[20]     = {0};
    size_t reply_len = sizeof(reply);
    snprintf(cmd, sizeof(cmd), "SELECT_NETWORK %d", Netid);
    CtrlRequest(cmd, reply, &reply_len);
    if (strncmp(reply, "OK", 2) != 0) {
        dzlog_debug("wifi SELECT_NETWORK %d cmd failed.\n", Netid);
        return -1;
    }
    return 0;
}

int RemoveSelectNetwork(int Netid)
{
    char reply[10] = {0};
    char cmd[20]   = {0};
    int ret        = 0;

    size_t reply_len = sizeof(reply);
    snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %d", Netid);
    ret = CtrlRequest(cmd, reply, &reply_len);
    if (ret < 0 || strncmp(reply, "OK", 2) != 0) {
        dzlog_debug("Failed to remove network from configure.\n");
        return -1;
    } else {
        ret = CtrlRequest("SAVE_CONFIG", reply, &reply_len);
        if (ret < 0 || strncmp(reply, "OK", 2) != 0) {
            dzlog_debug("save cinfo into configure failed.\n");
            return -1;
        }
        dzlog_debug("save:%s\n", reply);
    }

    return 0;
}

int StartScan()
{
    char reply[10]   = {0};
    size_t reply_len = sizeof(reply);

    int ret = CtrlRequest("SCAN", reply, &reply_len);
    if (ret == 0 && strncmp(reply, "OK", 2) == 0) {
        ret = 0;
    } else {
        ret = -1;
    }

    return ret;
}

int ConnectSpecificAP(char *ssid)
{
    int ret = redbee_wifi_netListWorks(ssid);
    if (ret < 0) {
        if ((ret = redbee_wifi_netListWorks(ssid)) < 0)
            return ret;
    }

    if (SelectNetwork(ret) < 0)
        return -1;

    return ret;
}

int connectB()
{
    char reply[10];
    size_t reply_len = sizeof(reply);

    return CtrlRequest("REASSOCIATE", reply, &reply_len);
}

int Disconnect()
{
    redbee_dhcp_release(ctrl_iface);
    char reply[10];
    size_t reply_len = sizeof(reply);
    return CtrlRequest("DISCONNECT", reply, &reply_len);
}

void GetLocalInfo()
{
#if 0
    int len = 0;
    int i = 0;
    char delims[] = "\n";
    char *tmpbuff = NULL;
    char reply[2048];
    size_t reply_len = sizeof(reply);
    CtrlRequest("STATUS", reply, &reply_len);

    tmpbuff = strtok(buf, delims);

    while(tmpbuff != NULL)
    {
        tmpbuff = strtok(NULL, delims); 
        char *bufstart = strstr(tmpbuff,ApInfo[i]);
        if(bufstart)
        {
            bufstart = bufstart + strlen(ApInfo[i]);
            if(i == 0)
            {
                memcpy(Apbuff[index].bssid,bufstart,20); 
                //dzlog_debug(">>>>Richd:%s\n",Apbuff[index].bssid);
            }
            else if(i == 1)
            {
                Apbuff[index].freq = atoi(bufstart); 
                //dzlog_debug(">>>>Richd:%d\n",Apbuff[index].freq);
            }
            else if(i == 2)
            {
                Apbuff[index].level = atoi(bufstart); 
                //dzlog_debug(">>>>Richd:%d\n",Apbuff[index].level);
            }
            else if(i == 3)
            {
                memcpy(Apbuff[index].flag,bufstart,50); 
                //dzlog_debug(">>>>Richd:%s\n",Apbuff[index].flag);
            }
            else if(i == 4)
            {
                memcpy(Apbuff[index].ssid,bufstart,32); 
                //dzlog_debug(">>>>Richd:%s\n",Apbuff[index].ssid);
            }
            i++;

        }
        if(i == 5)
        {
            dzlog_debug(">>>>Richd(%d):%s\n %d\n %d\n %s\n %s\n",index,Apbuff[index].bssid,Apbuff[index].freq,
                    Apbuff[index].level,Apbuff[index].flag,Apbuff[index].ssid);

            break;
        }
    }
#endif
}

void wpa_ctrl_close(struct wpa_ctrl *ctrl)
{
    if (ctrl == NULL)
        return;
    unlink(ctrl->local.sun_path);
    if (ctrl->s >= 0)
        close(ctrl->s);
    if (ctrl) {
        os_free(ctrl);
        ctrl = NULL;
    }
}

static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach)
{
    char buf[10] = {0};
    int ret;
    size_t len = 10;

    ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6, buf, &len, NULL);
    if (ret < 0)
        return ret;
    if (len == 3 && os_memcmp(buf, "OK\n", 3) == 0)
        return 0;
    return -1;
}

int wpa_ctrl_attach(struct wpa_ctrl *ctrl) { return wpa_ctrl_attach_helper(ctrl, 1); }

int wpa_ctrl_detach(struct wpa_ctrl *ctrl) { return wpa_ctrl_attach_helper(ctrl, 0); }

void *os_zalloc(size_t size) { return calloc(1, size); }

struct wpa_ctrl *wpa_ctrl_open2(const char *ctrl_path, const char *cli_path)
{
    struct wpa_ctrl *ctrl;
    static int counter = 0;
    int ret;
    size_t res;
    int tries = 0;
    int flags;

    if (ctrl_path == NULL)
        return NULL;

    ctrl = os_zalloc(sizeof(*ctrl));
    if (ctrl == NULL)
        return NULL;

    ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
    if (ctrl->s < 0) {
        os_free(ctrl);
        ctrl = NULL;
        return NULL;
    }

    ctrl->local.sun_family = AF_UNIX;
    counter++;
try_again:
    if (cli_path && cli_path[0] == '/') {
        ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), "%s/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", cli_path, (int)getpid(), counter);
    } else {
        ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), CONFIG_CTRL_IFACE_CLIENT_DIR "/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", (int)getpid(), counter);
    }
    if (os_snprintf_error(sizeof(ctrl->local.sun_path), ret)) {
        close(ctrl->s);
        os_free(ctrl);
        ctrl = NULL;
        return NULL;
    }
    tries++;
    if (bind(ctrl->s, (struct sockaddr *)&ctrl->local, sizeof(ctrl->local)) < 0) {
        if (errno == EADDRINUSE && tries < 2) {
            /*
             * getpid() returns unique identifier for this instance
             * of wpa_ctrl, so the existing socket file must have
             * been left by unclean termination of an earlier run.
             * Remove the file and try again.
             */
            unlink(ctrl->local.sun_path);
            goto try_again;
        }
        close(ctrl->s);
        os_free(ctrl);
        ctrl = NULL;
        return NULL;
    }

    ctrl->dest.sun_family = AF_UNIX;
    if (os_strncmp(ctrl_path, "@abstract:", 10) == 0) {
        ctrl->dest.sun_path[0] = '\0';
        os_strlcpy(ctrl->dest.sun_path + 1, ctrl_path + 10, sizeof(ctrl->dest.sun_path) - 1);
    } else {
        res = os_strlcpy(ctrl->dest.sun_path, ctrl_path, sizeof(ctrl->dest.sun_path));
        if (res >= sizeof(ctrl->dest.sun_path)) {
            close(ctrl->s);
            os_free(ctrl);
            ctrl = NULL;
            return NULL;
        }
    }
    if (connect(ctrl->s, (struct sockaddr *)&ctrl->dest, sizeof(ctrl->dest)) < 0) {
        close(ctrl->s);
        unlink(ctrl->local.sun_path);
        os_free(ctrl);
        ctrl = NULL;
        return NULL;
    }

    /*
     * Make socket non-blocking so that we don't hang forever if
     * target dies unexpectedly.
     */
    flags = fcntl(ctrl->s, F_GETFL);
    if (flags >= 0) {
        flags |= O_NONBLOCK;
        if (fcntl(ctrl->s, F_SETFL, flags) < 0) {
            perror("fcntl(ctrl->s, O_NONBLOCK)");
            /* Not fatal, continue on.*/
        }
    }

    return ctrl;
}

/**
 **function: wpa_ctrl_open
 **Describe:build a connect with wpa_supplicant
 **param: ctrl_path [ in ]---->the path of WLAN interface
 **return : the interface which is used to communicate with wpa_supplicant
 */
struct wpa_ctrl *wpa_ctrl_open(const char *ctrl_path) {
    return wpa_ctrl_open2(ctrl_path, NULL);
}

int str_match(const char *a, const char *b) { return strncmp(a, b, strlen(b)) == 0; }

int setNetworkParam(int id, const char *field, const char *value, bool quote)
{
    char reply[10] = {0}, cmd[256] = {0};
    size_t reply_len;
    snprintf(cmd, sizeof(cmd), "SET_NETWORK %d %s %s%s%s", id, field, quote ? "\"" : "", value, quote ? "\"" : "");
    reply_len = sizeof(reply);
    CtrlRequest(cmd, reply, &reply_len);
    return strncmp(reply, "OK", 2) == 0 ? 0 : -1;
}

/**
 **function: newHotspotSave
 **Describe:when connect a new AP or hot spot,save the info about hot spot
 **param: ssid [ in ]  the name of the new hot spot
 **param: password [ in ]  the password of the new hot spot  0 refer to there is no psk for the hot spot
 **param: security [ in ] the type of encrypt  WPA/WPA2
 */
int newHotspotSave(char *ssid, char *password, int security)
{
    char reply[10] = {0}, cmd[256] = {0};
    size_t reply_len;
    int id;
    int ret   = -1;
    char *psk = NULL;

    int psklen = strlen(password);
    int auth   = security;

    if (redbee_wifi_netListWorks(ssid) >= 0) {
        dzlog_debug("wifi it is already exist\n");
        redbee_wifi_get_psk(ssid, &psk);
        if (psk && password) {
            if (strcmp(password, psk) == 0) {
                dzlog_debug("psk is same----\n");
                return 0;
            } else {
                dzlog_debug("psk is changed----\n");
                redbee_wifi_removeSelectAP(ssid);
            }
        } else {
            dzlog_debug("password(%d):%s\n", atoi(password), password);
            if (atoi(password) == 0) {
                dzlog_debug("psk is open----\n");
                return 0;
            } else {
                dzlog_debug("psk is changed----\n");
                redbee_wifi_removeSelectAP(ssid);
            }
        }
    }

    dzlog_debug("wifi psklen = %d\n", psklen);

    if (auth == AUTH_WPA_PSK || auth == AUTH_WPA2_PSK) {
        if (psklen < 8 || psklen > 64) {
            dzlog_debug("wifi WPA-PSK requires a passphrase of 8 to 63 characters\n");
            return -1;
        }
    }

    memset(reply, 0, sizeof(reply));
    reply_len = sizeof(reply) - 1;

    CtrlRequest("ADD_NETWORK", reply, &reply_len);
    if (reply[0] == 'F') {
        dzlog_debug("wifi Failed to add network to wpa_supplicant configuration.\n");
        return -1;
    }
    id = atoi(reply);
    dzlog_debug("wifi New id =%d\n", id);

    ret = setNetworkParam(id, "ssid", ssid, true);
    if (ret < 0) {
        dzlog_debug("wifi set ssid(%s) error\n", ssid);
        return -1;
    }

    const char *key_mgmt = NULL, *proto = NULL, *pairwise = NULL;
    switch (auth) {
        case AUTH_NONE_OPEN:
        case AUTH_NONE_WEP:
        case AUTH_NONE_WEP_SHARED:
            key_mgmt = "NONE";
            break;
        case AUTH_IEEE8021X:
            key_mgmt = "IEEE8021X";
            break;
        case AUTH_WPA_PSK:
            key_mgmt = "WPA-PSK";
            proto    = "WPA";
            break;
        case AUTH_WPA_EAP:
            key_mgmt = "WPA-EAP";
            proto    = "WPA";
            break;
        case AUTH_WPA2_PSK:
            key_mgmt = "WPA-PSK";
            proto    = "WPA2";
            break;
        case AUTH_WPA2_EAP:
            key_mgmt = "WPA-EAP";
            proto    = "WPA2";
            break;
    }

    if (auth == AUTH_NONE_WEP_SHARED)
        setNetworkParam(id, "auth_alg", "SHARED", false);
    else
        setNetworkParam(id, "auth_alg", "OPEN", false);

    if (auth == AUTH_WPA_PSK || auth == AUTH_WPA_EAP || auth == AUTH_WPA2_PSK || auth == AUTH_WPA2_EAP) {
        int encr = 1;
        if (encr == 0)
            pairwise = "TKIP";
        else
            pairwise = "CCMP";
    }

    if (proto)
        setNetworkParam(id, "proto", proto, false);
    if (key_mgmt)
        setNetworkParam(id, "key_mgmt", key_mgmt, false);
    if (pairwise) {
        setNetworkParam(id, "pairwise", pairwise, false);
        setNetworkParam(id, "group", "TKIP CCMP WEP104 WEP40", false);
    }

    setNetworkParam(id, "psk", password, psklen != 64);
    if (ret < 0) {
        dzlog_debug("wifi set psk(%s) error\n", password);
        return -1;
    }

    snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %d", id);
    reply_len = sizeof(reply);
    CtrlRequest(cmd, reply, &reply_len);
    if (strncmp(reply, "OK", 2) != 0) {
        dzlog_debug("wifi Failed to enable network in wpa_supplicant configuration.\n");
        return -1;
    }

    CtrlRequest("SAVE_CONFIG", reply, &reply_len);
    dzlog_debug("reply:%s\n", reply);
    if (strncmp(reply, "OK", 2) != 0) {
        dzlog_debug("wifi Failed to save info into wpa_supplicant configuration.\n");
        return -1;
    }

    return 0;
}

int openCtrlConnect(const char *ifname)
{
    char *cfile = NULL;
    int flen    = 0;
    size_t len  = 0;

    if (ifname) {
        if (ifname != ctrl_iface) {
            if (ctrl_iface)
                free(ctrl_iface);
            ctrl_iface = NULL;
            ctrl_iface = strdup(ifname);
        }
    } else {
        struct dirent *dent;
        DIR *dir = opendir(ctrl_iface_dir);
        if (ctrl_iface)
            free(ctrl_iface);
        ctrl_iface = NULL;
        if (dir) {
            while ((dent = readdir(dir))) {
                if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN)
                    continue;

                if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
                    continue;
                dzlog_debug("Selected interface '%s'\n", dent->d_name);
                ctrl_iface = strdup(dent->d_name);
                break;
            }
            closedir(dir);
        }
    }

    if (ctrl_iface == NULL) {
        return -1;
    }

    flen  = strlen(ctrl_iface_dir) + strlen(ctrl_iface) + 2;
    cfile = (char *)malloc(flen);
    if (cfile == NULL) {
        free(ctrl_iface);
        ctrl_iface = NULL;
        return -1;
    }
    snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ctrl_iface);

    if (ctrl_conn) {
        wpa_ctrl_close(ctrl_conn);
        ctrl_conn = NULL;
    }

    if (monitor_conn) {
        wpa_ctrl_detach(monitor_conn);
        wpa_ctrl_close(monitor_conn);
        monitor_conn = NULL;
    }

    ctrl_conn = wpa_ctrl_open(cfile);
    if (ctrl_conn == NULL) {
        free(cfile);
        cfile = NULL;
        free(ctrl_iface);
        ctrl_iface = NULL;
        return -1;
    }
    monitor_conn = wpa_ctrl_open(cfile);
    free(cfile);
    cfile = NULL;
    if (monitor_conn == NULL) {
        wpa_ctrl_close(ctrl_conn);
        free(ctrl_iface);
        ctrl_iface = NULL;
        return -1;
    }
    if (wpa_ctrl_attach(monitor_conn)) {
        dzlog_debug("Failed to attach to wpa_supplicant\n");
        wpa_ctrl_close(monitor_conn);
        monitor_conn = NULL;
        wpa_ctrl_close(ctrl_conn);
        ctrl_conn = NULL;
        free(ctrl_iface);
        ctrl_iface = NULL;
        return -1;
    }

    return 0;
}

void InitWpa(void)
{
    ctrl_iface     = NULL;
    ctrl_conn      = NULL;
    monitor_conn   = NULL;
    ctrl_iface_dir = strdup("/var/run/wpa_supplicant");
}

void *scanThread(void *data)
{
    prctl(PR_SET_NAME, "WIFI_scan");
    dzlog_debug("WiFi scan thread created");
    pthread_detach(pthread_self());

    while (1) {
        if (!scanAction) {
            break;
        }
        if (0 != StartScan())
            goto SCAN_ERROR; /*if scan error, can't destroy the thread, just ignore result update, wait for next scan*/
        UpdateResult(scanAP);
    SCAN_ERROR:
        usleep(scanAP.scanSec ? (scanAP.scanSec * 1000 * 1000) : (15 * 1000 * 1000));
    };
    pthread_exit(NULL);
    dzlog_debug("WiFi scan thread exit");
}

void redbee_wifi_change_scanAP(int type, unsigned int value)
{
    if (type == SCAN_SEC) {
        scanAP.scanSec = value;
    } else if (type == CONNECT_SEC) {
        scanAP.connSec = value;
        dzlog_debug("scanAP.connSec = %d\n", scanAP.connSec);
    }
}

void redbee_wifi_scan(scanResultCb ScanCb, char scanSec, autoConnCb ConnCb, char connSec)
{
    pthread_t tid;

    memset(&scanAP, 0, sizeof(scan_t));
    scanAP.scanCb     = ScanCb;
    scanAP.scanSec    = scanSec;
    scanAP.autoConnCbCallback = ConnCb;
    scanAP.connSec    = connSec;
    if (!scanAction) {
        scanAction = true;
        pthread_create(&tid, NULL, scanThread, NULL);
    }

    return;
}

void redbee_wifi_scan_stop(void) { scanAction = false; }

void redbee_wifi_scan_interval(char sec) { scanAP.scanSec = sec; }

/**
 ** function: isWifiConnected
 ** Describe: whether a wifi connected currently
 ** param buf [ in ]-->the result of STATUS from wpa_supplicant
 ** return: true  wifi is connected  false  wifi is disconnected
 */
static bool isWifiConnected(char *buf)
{
    char delims1[] = "\n";
    char delims2[] = "=";
    char *tmpbuff  = NULL;
    char *statebuf = NULL;

    tmpbuff = redbee_strtok(buf, delims1);

    while (tmpbuff != NULL) {
        statebuf = strtok(tmpbuff, delims2);
        if (statebuf && strcmp(statebuf, "wpa_state") == 0) {
            statebuf = strtok(NULL, delims2);
            if (statebuf && strcmp(statebuf, "COMPLETED") == 0)
                return true;
        }
        tmpbuff = redbee_strtok(NULL, delims1);
    }
    return false;
}

char *Getssid(char *buf)
{
    char delims1[] = "\n";
    char delims2[] = "=";
    char *tmpbuff  = NULL;
    char *ssidbuf  = NULL;

    tmpbuff = redbee_strtok(buf, delims1);

    while (tmpbuff != NULL) {
        ssidbuf = strtok(tmpbuff, delims2);
        if (ssidbuf && strcmp(ssidbuf, "ssid") == 0) {
            ssidbuf = strtok(0, delims2);
            return ssidbuf;
        }
        tmpbuff = redbee_strtok(NULL, delims1);
    }
    return NULL;
}

char *redbee_wifi_connectSsid(void)
{
    char reply[512]        = {0};
    char reply_backup[512] = {0};
    int ret                = 0;

    size_t reply_len = sizeof(reply) - 1;
    ret              = CtrlRequest("STATUS", reply, &reply_len);
    if (ret < 0) {
        dzlog_debug("wifi Could not get status from wpa_supplicant");
        return NULL;
    }

    reply[reply_len] = '\0';
    strcpy(reply_backup, reply);
    if (isWifiConnected(reply)) {
        return Getssid(reply_backup);
    }

    return NULL;
}

int redbee_wifi_connectNewAP(char *ssid, char *passcode, int security, char sec)
{
    int ret = -1;
    struct os_reltime started_at;

    started_at.sec  = 0;
    started_at.usec = 0;
    os_get_reltime(&started_at);

    ret = newHotspotSave(ssid, passcode, security);

    if (ret < 0)
        goto SAVE_ERROR;

    if (0 > ConnectSpecificAP(ssid)) {
        goto CONNECT_ERROR;
    }

    do {
        struct os_reltime n;
        os_get_reltime(&n);

        if (redbee_wifi_connectSsid() != NULL) {
            break;
        }

        if (os_reltime_expired(&n, &started_at, sec)) {
            redbee_wifi_removeSelectAP(ssid);
            return -1;
        }
    } while (1);
    redbee_dhcp_get(ctrl_iface);

    return 0;

CONNECT_ERROR:
    dzlog_debug("wifi connect to %s error\n", ssid);
    return -2;

SAVE_ERROR:
    dzlog_debug("wifi connect information set error\n");
    return -1;
}

int redbee_wifi_disconnect(void) { return Disconnect(); }

int redbee_wifi_init(void)
{
    InitWpa();
    return openCtrlConnect(ctrl_iface);
}

int redbee_wifi_connectOldAP(char *ssid, char sec)
{
    int ret;
    struct os_reltime started_at;

    if (ssid == NULL) {
        return -1;
    }

    ret = redbee_wifi_netListWorks(ssid);
    if (ret < 0) {
        dzlog_debug("not found this ssid:%s\n", ssid);
        return -1;
    }

    ret = SelectNetwork(ret);
    if (ret < 0) {
        printf("select network failed!!!\n");
        return -1;
    }

    started_at.sec  = 0;
    started_at.usec = 0;
    os_get_reltime(&started_at);

    do {
        struct os_reltime n;
        os_get_reltime(&n);

        if (redbee_wifi_connectSsid() != NULL) {
            break;
        }

        if (os_reltime_expired(&n, &started_at, sec)) {
            printf("connect the ssid(%s) timeout\n", ssid);
            return -1;
        }
    } while (1);

    redbee_dhcp_get(ctrl_iface);
    return 0;
}

int redbee_wifi_removeSelectAP(char *ssid)
{
    int ret;

    ret = redbee_wifi_netListWorks(ssid);
    if (ret < 0) {
        dzlog_debug("not found this ssid:%s\n", ssid);
        return ret;
    }

    return RemoveSelectNetwork(ret);
}

int ParseSignalLevel(char *buf, char *selectssid, int *signalLevel)
{
    char *labelStr     = NULL;
    char itemCount     = 0;
    char delims[]      = "\n";
    char lableDelims[] = "=";
    char ssid[32]      = {0};
    int level          = 0;
    char *tmpbuff      = NULL;
    char *bufstart     = NULL;

    tmpbuff = redbee_strtok(buf, delims);

    while (tmpbuff != NULL) {
        bufstart = strtok(tmpbuff, lableDelims);
        if (bufstart && strcmp(bufstart, "level") == 0) {
            labelStr = strtok(NULL, lableDelims);
            if (labelStr != NULL) {
                level = atoi(labelStr);
                itemCount++;
            }
        }

        if (bufstart && strcmp(bufstart, "ssid") == 0) {
            labelStr = strtok(NULL, lableDelims);
            if (labelStr != NULL) {
                memcpy(ssid, labelStr, sizeof(ssid));
                itemCount++;
            }
        }

        if (itemCount == 2) {
            if (selectssid && strcmp(ssid, selectssid) == 0) {
                *signalLevel = level;
                dzlog_debug("level is %d\n", level);
                return 0;
            } else {
                itemCount = 0;
            }
        }

        tmpbuff = redbee_strtok(NULL, delims);
    }  // end while

    return -1;
}

int UpdateSignalLevel(char *ssid, int *signalLevel)
{
    char reply[1024] = {0};
    size_t reply_len;
    int index;
    char cmd[20] = {0};
    int ret      = 0;
    index        = 0;
    while (1) {
        snprintf(cmd, sizeof(cmd), "BSS %d", index++);
        if (index > 100) {
            ret = -1;
            break;
        }

        reply_len = sizeof(reply) - 1;
        if ((ret = CtrlRequest(cmd, reply, &reply_len)) < 0) {
            break;
        }

        reply[reply_len] = '\0';
        dzlog_debug("%s\n", reply);
        if (strlen(reply)) {
            ret = ParseSignalLevel(reply, ssid, signalLevel);
            dzlog_debug("update signal:%d-----\n", *signalLevel);
            if (ret == 0)
                break;
        }
        bzero(reply, sizeof(reply));
        usleep(10000);
    }
    return ret;
}

int redbee_wifi_get_signal(char *ssid, int *signalLevel) { return UpdateSignalLevel(ssid, signalLevel); }

#if 0
int EnableAll()
{
    char reply[10];
    size_t reply_len = sizeof(reply);
    CtrlRequest("ENABLE_NETWORK all", reply, &reply_len);
    dzlog_debug("Enable:%s\n",reply);
}

void* autoConnThread(void *arg)
{
    prctl(PR_SET_NAME, "WIFI_autoConn");
    autoConn_t APConn;
    struct os_reltime started_at;

    APConn = *(autoConn_t*)arg;	
    started_at.sec = 0;
    started_at.usec = 0;

    pthread_detach(pthread_self());

    os_get_reltime(&started_at);

    do{

        struct os_reltime n;
        os_get_reltime(&n);
        if(os_reltime_expired(&n, &started_at, APConn.connSec))
        {
            //SelectSignalAP(APConn);
            os_get_reltime(&started_at);
        }
        usleep(100000);
    }while(1);

    return NULL;
}

void redbee_wifi_autoConn(autoConnCb cb,char sec)
{	
    pthread_t tid;
    autoConn_t autoConnAP;

    autoConnAP.autoConnCb = cb;
    autoConnAP.connSec = sec;
    pthread_create(&tid, NULL, autoConnThread,&autoConnAP);
}

#endif

void redbee_wifi_close()

{
    memset(&scanAP, 0, sizeof(scan_t));
    scanAction = false;
    if (ctrl_conn) {
        wpa_ctrl_close(ctrl_conn);
        ctrl_conn = NULL;
    }

    if (monitor_conn) {
        wpa_ctrl_detach(monitor_conn);
        wpa_ctrl_close(monitor_conn);
        monitor_conn = NULL;
    }

    if (ctrl_iface_dir) {
        free(ctrl_iface_dir);
        ctrl_iface_dir = NULL;
    }

    if (ctrl_iface) {
        free(ctrl_iface);
        ctrl_iface = NULL;
    }

    pthread_mutex_destroy(&wifiMutex);
}
